//
//  TFCodingPatcher.m
//  TrainFinder
//
//  Created by jiajunchen on 11/25/14.
//
//

#import "TFCodingPatcher.h"
#import <objc/runtime.h>

static NSMutableArray *exclude = nil;

@implementation TFCodingPatcher

+ (void)initialize {
    [super initialize];
    
    if (!exclude) {
        exclude = [NSMutableArray new];
        [exclude addObject:@{@"className": @"TFScheme", @"keys": @[@"_historyOrder"]}];
    }
}

+ (void)patchClass:(Class)klass {
    SEL selector = @selector(encodeWithCoder:);
    
    if (!class_getInstanceMethod(klass, selector)) {
        class_addMethod(klass, selector, (IMP)encodeWithCoder, NULL);
    }
    
    selector = @selector(initWithCoder:);
    
    if (!class_getInstanceMethod(klass, selector)) {
        class_addMethod(klass, selector, (IMP)initWithCoder, NULL);
    }
}

void encodeWithCoder(id SELF, SEL _cmd, NSCoder *aCoder) {
    unsigned int count = 0;
    Ivar *pVar = class_copyIvarList([SELF class], &count);

    __block NSArray *excludeKeys = nil;
    [exclude enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL *stop) {
        if ([obj[@"className"] isEqualToString:NSStringFromClass([SELF class])]) {
            excludeKeys = obj[@"keys"];
            *stop = YES;
        }
    }];
    
    for (unsigned int i = 0; i < count; i++) {
        Ivar var = pVar[i];
        
        const char *name = ivar_getName(var);
        NSString *k = [[NSString alloc] initWithUTF8String:name];
        
        __block BOOL shoud = YES;
        [excludeKeys enumerateObjectsUsingBlock:^(NSString *keyName, NSUInteger idx, BOOL *stop) {
            if ([keyName isEqualToString:k]) {
                shoud = NO;
                *stop = YES;
            }
        }];
        
        if (!shoud) {
            continue;
        }
        
        id v = [SELF valueForKey:k];
        
        if (k && v) {
            if ([v isKindOfClass:[NSObject class]]) {
                [aCoder encodeObject:v forKey:k];
            } else {
                [aCoder encodeObject:[NSNumber numberWithInteger:(NSInteger)v] forKey:k];
            }
        }
    }
    
    free(pVar);
}

id initWithCoder(id SELF, SEL _cmd, NSCoder *aDecoder) {
    //    [super init];
    
    __block NSArray *excludeKeys = nil;
    [exclude enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL *stop) {
        if ([obj[@"className"] isEqualToString:NSStringFromClass([SELF class])]) {
            excludeKeys = obj[@"keys"];
            *stop = YES;
        }
    }];
    
    if (SELF) {
        unsigned int count = 0;
        Ivar *pVar = class_copyIvarList([SELF class], &count);
        
        for (unsigned int i = 0; i < count; i++) {
            Ivar var = pVar[i];
            
            const char *name = ivar_getName(var);
            NSString *k = [[NSString alloc] initWithUTF8String:name];
            
            __block BOOL shoud = YES;
            [excludeKeys enumerateObjectsUsingBlock:^(NSString *keyName, NSUInteger idx, BOOL *stop) {
                if ([keyName isEqualToString:k]) {
                    shoud = NO;
                    *stop = YES;
                }
            }];
            
            if (!shoud) {
                continue;
            }
            
            id v = [aDecoder decodeObjectForKey:k];
            
            if (k && v) {
                [SELF setValue:v forKey:k];
            }
        }
        
        free(pVar);
    }
    
    return SELF;
}

@end
